// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.leon.lfilepickerlibrary.ui;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.leon.lfilepickerlibrary.R;
import com.leon.lfilepickerlibrary.adapter.PathAdapter;
import com.leon.lfilepickerlibrary.filter.LFileFilter;
import com.leon.lfilepickerlibrary.model.ParamEntity;
import com.leon.lfilepickerlibrary.utils.FileUtils;
import com.leon.lfilepickerlibrary.utils.StringUtils;
import com.leon.lfilepickerlibrary.widget.EmptyRecyclerView;
import com.noober.floatmenu.FloatMenu;
import com.noober.floatmenu.MenuItem;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;

/**
 * 文件选择器主界面。
 *
 * @author Leon, revision By JackJiang
 */
public class LFilePickerActivity extends AppCompatActivity
{
    private final static String TAG = LFilePickerActivity.class.getSimpleName();

    /**
     * 回调常量：从系统文件选择器选择文件.
     */
    int CHOOSE_FILE_RESULT_FROM_ACTIVITY_Q = 1;

    /** 常量：弹出菜单id之"收到的文件" */
    private final int CONTEXT_MENU_ID_MYDEFAULT = Menu.FIRST;
    /** 常量：弹出菜单id之"系统存储卡" */
    private final int CONTEXT_MENU_ID_SYSTEM_SDCARD = Menu.FIRST+1;
    /** 仿微信的弹出浮动菜单的显示参考基准位置 */
    private Point floatMenuShowPoint = new Point();

    /** 当前系统目录类型 */
    private CurrentDirType currentDirType = CurrentDirType.myDefault;

    /** 文件列表 */
    private EmptyRecyclerView mRecylerView;
    /** 文件列表的adapter对象 */
    private PathAdapter mPathAdapter;

    /** 当列表为空时显示的组件 */
    private View mEmptyView;
    /** 当列表为空时显示的打开sdcar按钮 */
    private Button mEmptyOpenSDcardBtn;

    /** 显示当前目录路径的标签控件 */
    private TextView mTvPath;
//    /** 返回上一级目录组件 */
//    private TextView mTvBack;
    /** 多选或处于选择框模式时的确认"选中"结果按钮 */
    private Button mBtnOK;
    /** 返回按钮（即关闭按钮） */
    private Button mBtnBack;
    /** 菜单项点击区父布局 */
    private ViewGroup mLayoutMenu;
    /** 菜单项提示文本 */
    private TextView mViewMenuHint;

    // 当前进入的目录
    private String mCurrentPath;
    // 当前进入的目录下的所有文件列表
    private List<File> mCurrentFiles;
    // 多选模式下，选择的多个文件列表
    private ArrayList<String> mSelectedFiles = new ArrayList<String>();//存放选中条目的数据地址

    // 进入本activity时传进来的参数
    private ParamEntity paramEntityFromIntent;

    /** 文件类型过滤器 */
    private LFileFilter mFilter;

    //android2.0前需要重写onKeyDown方法才能实现，2.0及以后直接重写onBackPressed即可哦
    /**
     * 捕获back键，实现回退到上级目录的能力.
     */
    @Override
    public void onBackPressed()
    {
        backToParentDirectory();
    }

    // 点按事件捕获处理(重写父类的本方法，目的是保存仿微信的弹出浮动菜单的显示基准位置点，以便设置弹出菜单的起始坐标)
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev)
    {
        if(ev.getAction() == MotionEvent.ACTION_DOWN)
        {
            // 保存此时的长按点信息
            floatMenuShowPoint.x = (int) ev.getRawX();
            floatMenuShowPoint.y = (int) ev.getRawY();
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        if (!checkSDState())
        {
            Toast.makeText(this, R.string.NotFoundPath, Toast.LENGTH_SHORT).show();
            finish();
        }

        initDataFromIntent();
        initView();
        initDatas();
        initListener();

        // 设置状态栏文字颜色为深色（系统默认是白色）
        setStatusBarTextColorDark();
    }

    /**
     * 设置状态栏文字颜色为深色（系统默认是白色）。
     * <p>
     * 说明：之所以有时候要设置为深色，是因为有些界面，沉浸式效果时的背景用的是白色，
     * 系统默认的状态栏文字颜色也是白色，这样的话就看不清系统的时间等等内容了，太难看。
     * <p>
     * 兼容性：如果要兼容Android 6.0以下版本，则非常费事，因为各厂商（小米、oppo、魅族等等）
     * 的设置方法都不一样，而且也不统一。所以，为了保持代码逻辑的简洁简单，就只使用Android 6.0
     * 中官方的标准方法来实现。如果要在低版本手机上使用这个特性？不好意思，不支持！
     * <p>
     * 参考资料：
     * https://blog.csdn.net/woainijinying/article/details/82459131
     * https://www.jianshu.com/p/abd021c22728
     * https://www.jianshu.com/p/7392237bc1de
     */
    @TargetApi(Build.VERSION_CODES.M)
    private void setStatusBarTextColorDark()
    {
        this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    }

    /**
     * 进入本Activity时，从Intent中读取传进来的数据。
     */
    private void initDataFromIntent()
    {
        paramEntityFromIntent = (ParamEntity) getIntent().getExtras().getSerializable("param");
    }

    /**
     * 初始化控件
     */
    private void initView()
    {
        setContentView(R.layout.activity_lfile_picker);

        mRecylerView = (EmptyRecyclerView) findViewById(R.id.recylerview);
        mTvPath = (TextView) findViewById(R.id.tv_path);
//      mTvBack = (TextView) findViewById(R.id.tv_back);
        mBtnOK = (Button) findViewById(R.id.btn_addbook);
        mEmptyView = findViewById(R.id.empty_view);
        mEmptyOpenSDcardBtn = findViewById(R.id.btn_openSystemCard);
        mBtnBack = (Button)findViewById(R.id.lfilepickerlibrary_widget_title_left_backBtn);
        mLayoutMenu = (ViewGroup)findViewById(R.id.lfilepickerlibrary_widget_title_left_menuBtn);
        mViewMenuHint = (TextView) findViewById(R.id.lfilepickerlibrary_widget_title_left_menuHint);

        if (paramEntityFromIntent.getAddText() != null)
            mBtnOK.setText(paramEntityFromIntent.getAddText());

        updateOKButton();
    }

    /**
     * 初始化数据。
     */
    private void initDatas()
    {
        // 设置当前目录
        mCurrentPath = paramEntityFromIntent.getMyDefaultDir();
        // 如果用户传入默认目录则使用系统sd卡根目录作为当前目录
        if (StringUtils.isEmpty(mCurrentPath)) {
            mCurrentPath = _getRootDir_sdcard().getAbsolutePath();
        }

//      mTvPath.setText(mCurrentPath);
        setShowPath(mCurrentPath);

        mFilter = new LFileFilter(paramEntityFromIntent.getFileTypes());
        mCurrentFiles = FileUtils.getFileList(mCurrentPath, mFilter, paramEntityFromIntent.isGreater(), paramEntityFromIntent.getFileSize());
        mPathAdapter = new PathAdapter(mCurrentFiles, this, mFilter, paramEntityFromIntent.isMutilyMode(), paramEntityFromIntent.isGreater(), paramEntityFromIntent.getFileSize());
        mRecylerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecylerView.setAdapter(mPathAdapter);
        mRecylerView.setmEmptyView(mEmptyView);
    }

    /**
     * 添加点击事件处理
     */
    private void initListener()
    {
        mPathAdapter.setOnItemClickListener(position -> {
            if (paramEntityFromIntent.isMutilyMode()) {
                if (mCurrentFiles.get(position).isDirectory()) {
                    //如果当前是目录，则进入继续查看目录
                    chekInDirectory(mCurrentFiles.get(position).getAbsolutePath());
                    updateOKButton();
                } else {
                    //如果已经选择则取消，否则添加进来
                    if (mSelectedFiles.contains(mCurrentFiles.get(position).getAbsolutePath())) {
                        mSelectedFiles.remove(mCurrentFiles.get(position).getAbsolutePath());
                    } else {
                        mSelectedFiles.add(mCurrentFiles.get(position).getAbsolutePath());
                    }

                    updateOKButton();

                    //先判断是否达到最大数量，如果数量达到上限提示，否则继续添加
                    if (paramEntityFromIntent.getMaxNum() > 0 && mSelectedFiles.size() > paramEntityFromIntent.getMaxNum()) {
                        Toast.makeText(LFilePickerActivity.this, R.string.OutSize, Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
            } else {
                //单选模式直接返回
                if (mCurrentFiles.get(position).isDirectory()) {
                    chekInDirectory(mCurrentFiles.get(position).getAbsolutePath());
                    return;
                }

                if (paramEntityFromIntent.isChooseMode()) {
                    //选择文件模式,需要添加文件路径，否则为文件夹模式，直接返回当前路径
                    mSelectedFiles.add(mCurrentFiles.get(position).getAbsolutePath());
                    chooseDone();
                } else {
                    Toast.makeText(LFilePickerActivity.this, R.string.ChooseTip, Toast.LENGTH_SHORT).show();
                }
            }
        });

        mBtnOK.setOnClickListener(v -> {
            if (paramEntityFromIntent.isChooseMode() && mSelectedFiles.size() < 1) {
                String info = paramEntityFromIntent.getNotFoundFiles();
                if (TextUtils.isEmpty(info)) {
                    Toast.makeText(LFilePickerActivity.this, R.string.NotFoundBooks, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LFilePickerActivity.this, info, Toast.LENGTH_SHORT).show();
                }
            } else {
                //返回
                chooseDone();
            }
        });

        // 返回按钮（即关闭按钮）点击事件处理
        mBtnBack.setOnClickListener(v -> finish());

        // 菜单项点击事件处理
        mLayoutMenu.setOnClickListener(v -> {
            // 准备好要弹出的菜单item
            final ArrayList<MenuItem> menuItemList  = new ArrayList();{
                menuItemList.add(new MenuItem().setItem("聊天中的文件").setItemActionId(CONTEXT_MENU_ID_MYDEFAULT));
                menuItemList.add(new MenuItem().setItem("系统的存储卡").setItemActionId(CONTEXT_MENU_ID_SYSTEM_SDCARD));
            }

            // 准备好弹出菜单对象
            FloatMenu floatMenu = new FloatMenu(LFilePickerActivity.this);
            // 添加要显示的菜单项
            floatMenu.items(menuItemList);
            // 菜单项点击事件处理
            floatMenu.setOnItemClickListener((v1, menuItemPosition) -> {
                MenuItem mi = menuItemList.get(menuItemPosition);
                if(mi != null) {
                    switch (mi.getItemActionId()) {
                        // 切换到收到的文件
                        case CONTEXT_MENU_ID_MYDEFAULT: {
                            gotoRecievedFiles();
                            break;
                        }
                        // 切换到系统存储卡
                        case CONTEXT_MENU_ID_SYSTEM_SDCARD: {
                            gotoSDCard();
                            break;
                        }
                    }
                }
            });

            // 显示弹出菜单
            floatMenu.show(floatMenuShowPoint);
        });

        // 当本地文件列表为空时显示的打开系统存储卡按钮事件处理
        mEmptyOpenSDcardBtn.setOnClickListener(v -> gotoSDCard());
    }

    // 查看本地收到的文件
    private void gotoRecievedFiles() {
        if(currentDirType != CurrentDirType.myDefault) {
            currentDirType = CurrentDirType.myDefault;
            // 切换到"收到的文件"目录
            switchToDirectory(_getRootDir_mydefault().getAbsolutePath(), true);
            mViewMenuHint.setText("聊天中的文件");
        }
    }

    // 进入系统存储卡
    private void gotoSDCard() {
        // 当系统为Andriod Q及以上版本时，将调用系统文件选择器，并进行系统文件选择逻辑处理
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            openSystemFileChooserForQ();
        }
        // Andriod Q以下的系统，继续之前的读取SD卡的方式，浏览所有手机
        else {
            if (currentDirType != CurrentDirType.systemSDCard) {
                currentDirType = CurrentDirType.systemSDCard;
                // 切换成系统存储卡
                switchToDirectory(_getRootDir_sdcard().getAbsolutePath(), true);

                mViewMenuHint.setText("系统的存储卡");
            }
        }
    }

    /**
     * Andriod Q及以上版本因系统分区存储权限限制，无法直接读取SD卡中的File对象，本方法实现的是
     * 从系统文件选择器中读取文件并返Uri的方式进行文件选择。
     */
    protected void openSystemFileChooserForQ(){
        FileUtils.chooseSystemFile(this, CHOOSE_FILE_RESULT_FROM_ACTIVITY_Q);
    }

    /**
     * 刷新多选或勾选模式下的确认按钮UI显示。
     */
    private void updateOKButton()
    {
        if (!paramEntityFromIntent.isMutilyMode())
        {
            mBtnOK.setVisibility(View.GONE);
        }

        if (!paramEntityFromIntent.isChooseMode())
        {
            mBtnOK.setVisibility(View.VISIBLE);
            mBtnOK.setText(getString(R.string.OK));
            //文件夹模式默认为单选模式
            paramEntityFromIntent.setMutilyMode(false);
        }

        if (paramEntityFromIntent.getAddText() != null)
        {
            mBtnOK.setText(paramEntityFromIntent.getAddText() + "( " + mSelectedFiles.size() + " )");
        }
        else
        {
            mBtnOK.setText(getString(R.string.Selected) + "( " + mSelectedFiles.size() + " )");
        }
    }

    /**
     * 返回上一级目录。
     */
    private void backToParentDirectory()
    {
        String tempPath = new File(mCurrentPath).getParent();

        Log.d(TAG, "正在返回上一级："+tempPath);

        if (tempPath == null)
        {
            Log.w(TAG, "反回的上一级目录为null？！");
            finish();
            return;
        }
        else
        {
            // 如果当前目录已经根目录，返手机上的返回软键盘就直接关闭当前界面了
            if(tempPath.equals(getRootDir().getParent()))
            {
                Log.i(TAG, "当前已是根目录了，不能再退回上级了，直接退出当前界面哦。");
                finish();
                return;
            }
        }

        switchToDirectory(tempPath, false);
        updateOKButton();
    }

    /**
     * 切换到新的目录。
     * <p>
     * 比如：返回上一级目录时、切换"收到的文件"和"系统存储"功能时。
     * </p>
     */
    private void switchToDirectory(String destPath, boolean cleareSelectedFilesInMultiMode)
    {
        chekInDirectory(destPath);

        if(cleareSelectedFilesInMultiMode)
        {
            //清除添加集合中数据
            mSelectedFiles.clear();
        }
    }

    /**
     * 进入下一级目录.
     */
    private void chekInDirectory(String destPath)//int position)
    {
//        mCurrentPath = mCurrentFiles.get(position).getAbsolutePath();
        mCurrentPath = destPath;
        setShowPath(mCurrentPath);
        //更新数据源
        mCurrentFiles = FileUtils.getFileList(mCurrentPath, mFilter
                , paramEntityFromIntent.isGreater(), paramEntityFromIntent.getFileSize());
        mPathAdapter.setmListData(mCurrentFiles);
        mPathAdapter.notifyDataSetChanged();
        mRecylerView.scrollToPosition(0);

        mPathAdapter.updateAllSelelcted(false);
    }

    /**
     * 完成文件选择。
     */
    private void chooseDone()
    {
        //判断是否数量符合要求
        if (paramEntityFromIntent.isChooseMode())
        {
            if (paramEntityFromIntent.getMaxNum() > 0 && mSelectedFiles.size() > paramEntityFromIntent.getMaxNum())
            {
                Toast.makeText(LFilePickerActivity.this, R.string.OutSize, Toast.LENGTH_SHORT).show();
                return;
            }
        }

        Intent intent = new Intent();
        intent.putStringArrayListExtra("paths", mSelectedFiles);
        intent.putExtra("path", (String)mTvPath.getTag());//, mTvPath.getText().toString().trim());
        intent.putExtra("used", "0");// 值为"0"表示是从本自定义文件选择器中返回
        setResult(RESULT_OK, intent);
        this.finish();
    }

    /**
     * 完成文件选择（通过Acitvity回调的方式，从系统文件选择器中选中要发送的大文件后的回调（用于Andriod Q及以上系统时））。
     * {@inheritDoc}
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // 从系统文件选择器中选中要发送的大文件后的回调（用于Andriod Q及以上系统时）
		if(resultCode == RESULT_OK && requestCode == CHOOSE_FILE_RESULT_FROM_ACTIVITY_Q)
        {
            // 将系统文件选择器中选中的文件数据通过Activity的回调接力传回给用户的调用界面
            data.putExtra("used", "1");// 值为"1"表示是从系统选择器中返回
            setResult(RESULT_OK, data);// 将系统文件选择器返回的intent直接返回给用户调用activity！
            this.finish();
        }
    }

    private File _getRootDir_sdcard()
    {
        return Environment.getExternalStorageDirectory();
    }

    private File _getRootDir_mydefault()
    {
        return new File(paramEntityFromIntent.getMyDefaultDir());
    }

    /**
     * 返回当前目录的根目录。
     *
     * @return
     */
    private File getRootDir()
    {
        File f = null;
        switch (this.currentDirType)
        {
            case myDefault:
                f = _getRootDir_mydefault();
                break;
            case systemSDCard:
            default:
                f = _getRootDir_sdcard();
                break;
        }

        return f;
    }

    /**
     * 检测SD卡是否可用
     */
    private boolean checkSDState()
    {
        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    }

    /**
     * 显示顶部地址
     *
     * @param path
     */
    private void setShowPath(String path)
    {
        // 保存真正的路径
        mTvPath.setTag(path);

        // 仿照微信那样，完整的目录路径显示时去掉设置的根路径（仅为了看起来清爽一点，没别的作用）
        String rootPath = getRootDir().getAbsolutePath();
        if(path != null && rootPath != null)
        {
            path = path.substring(rootPath.length(), path.length());
        }

        if(path == null || path.length() == 0) {
            switch (this.currentDirType) {
                case myDefault:
                    path = "当前聊天中的文件";
                    break;
                case systemSDCard:
                default:
                    path = "当前正位于根目录";
                    break;
            }
        }

        mTvPath.setText(path);
    }


    /**
     * 当前目录类型。
     */
    private enum CurrentDirType
    {
        /** "收到的文件" */
        myDefault,
        /** 系统存储卡 */
        systemSDCard
    }

}
